﻿module PathFinding

open SI
open Math
open Tile
open Chessboard
open ListUtility

type PathTreeNode = 
    {
        mTile   : Tile
        mDistance : float32<tile>
        mMoves : POSITION list 
    }

    static member Create(iTile, iMoves, iDistance) = 
        let res = 
            {
                mTile = iTile
                mMoves = iMoves
                mDistance = iDistance
            }
        res

let rec remove(node : PathTreeNode, tempList : PathTreeNode list) = 
    let head = tempList.Head
    match node with 
    | x when x = head -> tempList.Tail
    | _ -> tempList.Head :: remove(node, tempList.Tail)

type PathFinder = 
    {
        mutable mTree : PathTreeNode list
        mutable mMoves : POSITION list
    }

    static member CreatePathFinder() = 
        let res = 
            {
                mTree = []
                mMoves = []
            }
        res

    member this.Distance(iNode : Vector2<tile>, iOther : Vector2<tile>) = 
        System.Math.Abs( float32(iNode.X - iOther.X + iNode.Y - iOther.Y) )  * 1.f<tile>     

    member this.BestChoice(iChoise : PathTreeNode list) = 
        let mutable best = iChoise.Head
        for item in iChoise.Tail do
            let itemSum = item.mDistance + float32(item.mMoves.Length) * 1.f<tile>
            let bestSum = best.mDistance + float32(best.mMoves.Length) * 1.f<tile>
            if itemSum < bestSum then
                best <- item
        best

    member this.IsTileInTree(iTile) = 
        let mutable res = false
        for node in this.mTree do
            if iTile.Equals(node.mTile) then
                res <- true
        res

    member this.GetMovesToTile(iTile) = 
        let mutable res = []
        if (this.IsTileInTree(iTile)) then
            for node in this.mTree do
                if iTile.Equals(node.mTile) then
                    res <- node.mMoves
        res

    member this.PathFinding(iStart, iEnd, iChessboard : Chessboard) = 
        let mutable edgeNodes = []
        this.mMoves <- []
        this.mTree <- []
        let mutable res = false

        let startNode = PathTreeNode.Create(iStart, [], this.Distance(iStart.mPosition, iEnd.mPosition))
        edgeNodes <- startNode :: edgeNodes
        this.mTree <- startNode :: this.mTree
//        let neighbors = iChessboard.GetNeighbors(iStart)
//        for item in neighbors do
//            let step = iStart.IsTileCloseTo(item)
//            if (step <> POSITION.None) then
//                let tempMoves = step :: []
//                let newNode = PathTreeNode.Create(item, tempMoves, this.Distance(item.mPosition, iEnd.mPosition))
//                if (item.Equals(iEnd)) then
//                    res <- true
//                    this.mTree <- newNode :: this.mTree
//                else
//                    edgeNodes <- newNode :: edgeNodes
        while (edgeNodes.Length <> 0) do
            let choice = this.BestChoice(edgeNodes)
            this.mTree <- choice :: this.mTree
            edgeNodes <- remove(choice, edgeNodes)
            let neighbors = iChessboard.GetNeighbors(choice.mTile)
            for item in neighbors do
                if (item.Equals(iStart) <> true && this.IsTileInTree(item) <> true) then
                    let mutable tempMoves =
                        [
                            for item in choice.mMoves
                                -> item
                        ] 
                    let step = choice.mTile.IsTileCloseTo(item)
                    if (step <> POSITION.None) then
                        tempMoves <- step :: tempMoves
                        let newNode = PathTreeNode.Create(item, tempMoves, this.Distance(item.mPosition, iEnd.mPosition))
                        edgeNodes <- newNode :: edgeNodes
                        if (item.Equals(iEnd)) then
                            res <- true
                            this.mMoves <- tempMoves
        res

